package com.pj.project.sys_user_sync.brd;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import com.dtflys.forest.Forest;
import com.pj.current.config.SystemObject;
import com.pj.project.sys_user.SysUser;
import com.pj.project.sys_user_sync.Sm4Util;
import com.pj.project4sp.SP;
import com.pj.project4sp.config.SpConfigUtil;
import com.pj.utils.sg.AjaxError;
import com.pj.utils.so.SoMap;

import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.sso.SaSsoManager;
import cn.dev33.satoken.util.SaFoxUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 用户同步工具类，向外广播 
 * 
 * @author kong
 * @since 2022-11-22
 */
@Slf4j
public class SspServerBrdUtil {
	
	/** 添加用户接口 */
	public static final String BRD_USER_ADD = "/sso/brd/user/add";

	/** 删除用户接口 */
	public static final String BRD_USER_DELETE = "/sso/brd/user/delete";

	/** 修改用户资料 */
	public static final String BRD_USER_UPDATE = "/sso/brd/user/update";

	/** 修改用户密码 */
	public static final String BRD_USER_UPDATE_PASSWORD = "/sso/brd/user/updatePassword";
	

	// 向外广播：添加一个用户 
	public static void userAdd(SysUser s, String password) {
		
		// 如果尚未打开向外广播配置，则直接退出 
		if(SpConfigUtil.isBrd() == false) {
			return;
		}

		// 构建 Data 对象
		SoMap dataMap = SoMap.getSoMap()
				.setModel(s)
				.set("password", password);
		String createTime = dataMap.get("createTime").toString();
		String stringToDate = convertStringToDate(createTime);
		dataMap.put("createTime",stringToDate);

		// 推送广播 
		brdData(BRD_USER_ADD, dataMap);
	}

	// 向外广播：删除一个用户 
	public static void userDelete(long id) {
		
		// 如果尚未打开向外广播配置，则直接退出 
		if(SpConfigUtil.isBrd() == false) {
			return;
		}

		// 构建 Data 对象
		SoMap dataMap = SoMap.getSoMap()
				.set("id", id);
		
		// 推送广播 
		brdData(BRD_USER_DELETE, dataMap);
	}

	// 向外广播：修改一个用户 
	public static void userUpdate(SysUser s) {
		
		// 如果尚未打开向外广播配置，则直接退出 
		if(SpConfigUtil.isBrd() == false) {
			return;
		}

		// 构建 Data 对象
		SoMap dataMap = SoMap.getSoMap()
				.setModel(s)
				.delete("password");
		
		// 推送广播 
		brdData(BRD_USER_UPDATE, dataMap);
	}

	// 向外广播：指定用户修改了密码  
	public static void userUpdatePassword(long id, String password) {
		
		// 如果尚未打开向外广播配置，则直接退出 
		if(SpConfigUtil.isBrd() == false) {
			return;
		}

		// 构建 Data 对象
		SoMap dataMap = SoMap.getSoMap()
				.set("id", id)
				.set("password", password);
		
		// 推送广播 
		brdData(BRD_USER_UPDATE_PASSWORD, dataMap);
	}
	
	
	// ----------------- 
	
	/**
	 * 向外广播数据 
	 * 
	 * @param apiPath 广播接口地址 
	 * @param dataMap 广播的数据 Map 
	 */
	public static void brdData(String apiPath, Map<String, Object> dataMap) {

		/*
		 * 构建请求参数Map，形如：
		 * {
		 * 		data: 230ef68683b7fa2c5c1eabb9249412d,   // 数据字符串, SM4加密 
		 * 		timestamp: "1668965686992",   // 时间戳，13位，String格式 
		 * 		nonce: "WaetF4SEtSCaRXAIHmMGFvH0qAAyS7KW",   // 随机字符串,32位 
		 * 		sign: "5acdde80919b9cba08bcc2cc2d997192"   // 签名字符串，防篡改、防重放
		 * }
		 */
		SoMap paramMap = SoMap.getSoMap();
		
		// s1、数据 Map 转 JSON 串
		String dataJson = SaManager.getSaJsonTemplate().toJsonString(dataMap); 
		
		// s2、SM4 加密
		String dataStr = dataJson;
		if(SpConfigUtil.isBrdEncrypt()) {
			dataStr = Sm4Util.encrypt(dataJson, SystemObject.config.getSm4Key());
		}
		
		// s3、写入参数Map
		paramMap.set("data", dataStr);
		
		// s4、给 参数Map 增加 sign 参数，防篡改、防重放
		String secretkey = SaSsoManager.getConfig().getSecretkey();
		SaManager.getSaSignTemplate().addSignParams(paramMap, secretkey);
		
		// s5、参数Map 转 参数字符串，形如：data=xxx&nonce=xxx&timestamp=xxx&sign=xxx
		String paramStr = SaManager.getSaSignTemplate().joinParams(paramMap);
		
		// ------------------------ 开始向外广播 
		
		// 同步广播 
		String syncBrdUrl = SpConfigUtil.brdUrlSync();
		// System.out.println("syncBrdUrl " + syncBrdUrl);
		if(SaFoxUtil.isNotEmpty(syncBrdUrl)) {

			// 发送请求 
			String fullUrl = SaFoxUtil.spliceTwoUrl(syncBrdUrl, apiPath);
			try {
				String resBody = Forest.post(fullUrl).addBody(paramStr).executeAsString();
				
				// 判断结果 
				SoMap resMap = SoMap.getSoMap().setJsonString(resBody);
				if(200 == resMap.getInt("code") && "ok".equals(resMap.getString("msg"))){
					log.info("数据广播成功(同步: {}) data={}", fullUrl, dataJson);
				} else {
					throw new AjaxError(resMap.getString("msg"));
				}
			} catch (Exception e) {
				log.info("数据广播失败(同步: {}) data={}", fullUrl, dataJson);
				throw new AjaxError("用户广播失败：" + e.getMessage(), e);
			}

		}
		
		// 异步广播 
		List<String> brdUrlList = SpConfigUtil.brdUrl();
		for (String brdUrl : brdUrlList) {
			if(SaFoxUtil.isNotEmpty(brdUrl)) {
				SP.taskExecutor.execute(() -> {//@TODO "http://172.168.1.118:8888"
					String fullUrl = SaFoxUtil.spliceTwoUrl(brdUrl, apiPath);
					try {
						Forest.post(fullUrl).addBody(paramStr).execute();
						log.info("数据广播成功(异步: {}) data={}", fullUrl, dataJson);
					} catch (Exception e) {
						log.info("数据广播失败(异步: {}) data={}", fullUrl, dataJson);
						e.printStackTrace();
					}
				});
			}
		}
		
	}


	// 这个方法接受一个字符串日期，返回一个 String 对象
	public static String convertStringToDate(String dateString) {
		DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ENGLISH);
		DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
		try {
			LocalDateTime localDateTime = LocalDateTime.parse(dateString, inputFormatter);
			return outputFormatter.format(localDateTime);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
}
